#ifndef PARAFLUIDTRIANGLEVOXELIZER_H
#define PARAFLUIDTRIANGLEVOXELIZER_H

#ifndef MIN_SLOTS_PER_BUCKET
#define MIN_SLOTS_PER_BUCKET 1
#endif

#include "parafluidtriangle.h"
#include "parafluidvector.h"

#include <QSet>
#include <QVector>

namespace ParaFluid
{
	
	/**
	* Triangle Voxelizer
	*/
	class TriangleVoxelizer
	{
		
	public:
		
		/**
		* Read-Only Visitor
		*/
		class ReadOnlyVisitor
		{
			
		public:
			
			/**
			* Destructor
			*/
			virtual ~ReadOnlyVisitor() { /* does nothing */ }
			
			/**
			* Visit a Triangle
			*/
			void visit(const Triangle * triangle);
			
			/**
			* Visit a unique Triangle
			*/
			virtual void visitUnique(const Triangle * triangle) = 0;
			
			/**
			* Clear Visited Triangles
			*/
			void clear();
			
		private:
			
			/**
			* Visited
			*/
			mutable QSet<const Triangle *> _visited;
			
		};
		
		/**
		* Bucket
		* An Hybrid Bucket that hold a pre-defined number of Slots
		* and all the extra Slots in a Vector
		*/
		class Bucket		{
		
		public:
			
			/**
			* Destructor
			*/
			~Bucket();
			
			/**
			* Default Constructor
			*/
			Bucket();
			
			/**
			* Copy Constructor
			*/
			Bucket(const Bucket & other);
			
			/**
			* Assignment Operator
			*/
			Bucket & operator= (const Bucket & rhs);
			
			/**
			* Getter for the number of Occupied Slots
			*/
			int occupiedSlots() const;
			
			/**
			* Put the specified Triangle in the next Slot
			* @complexity Amortized O(1)
			*/
			void append(const Triangle * triangle);
			
			/**
			* Remove all Atoms from all Slots
			* @complexity O(n)
			*/
			void clear();
			
			/**
			* Accept Visitor for all Atoms of all Slots
			*/
			void acceptVisitor(ReadOnlyVisitor & visitor) const;
			
		private:
			
			/**
			* Number of Reserved Slots that are Occupied by an Atom
			*/
			int _reservedSlotsOccupied;
			
			/**
			* Reserved Slots
			*/
			const Triangle * _reservedSlots[MIN_SLOTS_PER_BUCKET];
			
			/**
			* Extra Slots
			*/
			QVector<const Triangle *> _extraSlots;
			
		};
		
		/**
		* Destructor
		*/
		~TriangleVoxelizer();
		
		/**
		* Default Constructor
		*/
		TriangleVoxelizer();
		
		/**
		* Constructor with specified number of Buckets
		*/
		TriangleVoxelizer(int buckets, float smoothingDistance);
		
		/**
		* Copy Constructor
		*/
		TriangleVoxelizer(const TriangleVoxelizer & other);
		
		/**
		* Assignment Operator
		*/
		TriangleVoxelizer & operator= (const TriangleVoxelizer & rhs);
		
		/**
		* Getter for Smoothing Distance
		*/
		const float & smoothingDistance() const;
		
		/**
		* Getter for the Number of Triangles
		*/
		int count() const;
		
		/**
		* Insert the specified Triangle
		*/
		void insert(const Triangle & triangle);
		
		/**
		* Remove all Atoms
		*/
		void clear();
		
		/**
		* Accept Visitor for all Atoms
		*/
		void acceptVisitor(ReadOnlyVisitor & visitor) const;
		
		/**
		* Accept Visitor for Atoms in the Voxel at the specified Position
		*/
		void acceptVisitor(ReadOnlyVisitor & visitor, const Vector & position) const;
		
		/**
		* Voxelize a Position with Smoothing Distance
		*/
		Vector voxelize(const Vector & position) const;
		
	private:
		
		/**
		* Hash Function
		*/
		unsigned int hash(const Vector & voxel) const;
		
		/**
		* Smoothing Distance
		*/
		float _smoothingDistance;
		
		/**
		* Buckets
		*/
		QVector<Bucket> _buckets;
		
		/**
		* Count
		*/
		int _count;
		
	};
	
}

#endif

